#include <iostream>

/**
 * c++模板
 *
 *
 */
using namespace std;

// 一、函数模板
// 函数模板的定义格式
//template <typename 形参名, typename 形参名...>     //模板头（模板说明）
//返回值类型  函数名（参数列表）                   //函数定义
//{
//        函数体;
//}
//注意注意：
//1，template是声明模板的关键字，告诉编译器开始泛型编程。
//2，尖括号<>中的typename是定义形参的关键字，用来说明其后的形参名为类型 参数,（模板形参）。Typename（建议用）可以用class关键字代替，
// 两者没有区别。
//3，模板形参（类属参数）不能为空（俗成约定用一个大写英文字母表示），且在函 数定义部分的参数列表中至少出现一次。与函数形参类似，可以用在
// 函数定义的各 个位置：返回值、形参列表和函数体。
//4，函数定义部分：与普通函数定义方式相同，只是参数列表中的数据类型要使用 尖括,号<>中的模板形参名来说明。当然也可以使用一般的类型参数。

template <typename T>   // 模板头，template关键字告诉编译器开始泛型编程
T add(T t1, T t2) {     // 类型参数化为T
    return t1 + t2;
}

// 函数模板实例化
// 1.隐式初始化
// 直接调用泛型函数时，会在运行时编译创建一个泛型函数的实例，这就是泛型函数的隐式实例化
// 2.1.显式实例化，类似于函数重载的方式，指定参数的实际类型
int add(int a, int b) {
    cout << "调用add(int,int)" << endl;
    return a + b;
}
// 2.2.显式实例化，template 函数返回值类型 函数名<实例化的类型>(参数列表);
//注意：这是声明语句，要以分号结束，<>中是显式实例的数据类型，即要实例 化出一个什么类型的函数。如：显示实例化为int，则在调用时，不是int 类型
// 的数据会 转换为int类型进行计算。
template double add<double>(double a, double b); // 可以这么写，但是应该没什么实际作用

// 使用函数模板要注意的问题，函数模板中的每一个类型参数在函数参数表中必须至少使用一次。


// 二、类模板
// 定义类模板的格式
//template<typename 形参名,typename 形参名…>
//class 类名
//{
//        ………
//}
//说明：
//（1）类模板中的关键字含义与函数模板相同。
//（2）类模板中的类型参数可用在类声明和类实现中。类模板的模板形参（类型参 数）不能为空，一旦声明了类模板就可以用类模板的形参名声明类中
// 的成员变量和 成员函数，即在类中使用内置数据类型的地方都可以使用模板形参名来代替。
template <typename T1, typename T2>
class A {
public:
    T1 t1;
    T2 t2;
    A(T1 t1, T2 t2):t1(t1),t2(t2) {
        cout << "constructor A(T1 t1, T2 t2)" << endl;
    }
    void Print() {
        cout << "t1=" << t1 << ",t2=" << t2 << endl;
    }
};
// 类模板使用
void test2() {
    // 类模板的实例化，类A中凡是用到模板形参的地方都会被int类型所代替
    // 强调：与函数模板不同的是，类模板在实例化时，必须在尖括号中为模板形参显式 地指明数据类型（实参），编译器不能根据给定的数据
    // 推演出数据类型。即：不存 在将整型值10推演为int类型传递给模板形参的实参推演过程，必须要在<>中指 定int类型。
    A<int,int> a(2,3);
    a.Print();
    // 也可以使用指针方式实例化类模板
    auto *b = new A<int,int>(4,5);
    b->Print();
    delete b;
}






int main() {
    // 调用泛型函数
    // int类型实参
    int a = 1, b = 2;
    int c = add(a, b);
    cout << "c=" << c << endl;
    int d = add(3, 4);
    cout << "d=" << d << endl;

    // double类型实参
    double e = 10.1, f = 20.2;
    double g = add(e, f);
    cout << "g=" << g << endl;
    double h = add(30.3, 50.5);
    cout << "h=" << h << endl;


    // 类模板测试
    test2();



    return 0;
}


